package com.ibeeking.found.open.b.rest.service.impl;

import com.ibeeking.found.common.config.feign.GlobalClient;
import com.ibeeking.found.common.entity.TenantDTO;
import com.ibeeking.found.common.enums.parking.ParkingReturnCodeEnum;
import com.ibeeking.found.open.api.common.dto.ParkingPlatformDTO;
import com.ibeeking.found.open.api.common.dto.StatisticsCarDTO;
import com.ibeeking.found.open.api.common.dto.TenantParkingInfoDTO;
import com.ibeeking.found.open.api.common.param.ParkingChargeRecordParam;
import com.ibeeking.found.open.api.common.param.ParkingInParam;
import com.ibeeking.found.open.api.common.param.ParkingOrderParam;
import com.ibeeking.found.open.api.common.param.ParkingOutParam;
import com.ibeeking.found.open.api.common.query.ParkingConfigQuery;
import com.ibeeking.found.open.api.feign.ParkingFeignClient;
import com.ibeeking.found.open.b.rest.service.IParkingService;
import com.ibeeking.found.open.service.common.dto.ParkingResultDTO;
import com.ibeeking.found.open.service.common.param.ParkingParam;
import com.ibeeking.found.open.service.common.query.StatisticsCarQuery;
import com.ibeeking.nematos.constants.enums.LogConstant;
import com.ibeeking.nematos.exception.DataNotExistException;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.encryption.EncryptUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @ClassName ParkingServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2021-06-01 18:59
 **/
@Service
public class ParkingServiceImpl implements IParkingService {
    private static final String PARK_IN = "PARK_IN";
    private static final String PARK_OUT = "PARK_OUT";
    private static final String PARK_ORDER = "PARK_ORDER";
    private static final String PARK_CHARGE = "PARK_CHARGE";
    private static final String LOCK = "_LOCK:";
    private static final Long TIME = 24 * 60 * 60L;
    @Resource
    private ParkingFeignClient parkingFeignClient;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private GlobalClient globalClient;

    @Override
    public ParkingResultDTO parkIn(ParkingParam parkingParam) {
        List<ParkingInParam> parkingInParams = JsonUtils.jsonToList(parkingParam.getDataItems(), ParkingInParam.class);
        if (Collections3.isEmpty(parkingInParams)) {
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        }
        TenantParkingInfoDTO tenantParkingInfoDTO = getTenantId(parkingInParams.get(0).getParkCode());
        String tenantId = String.valueOf(tenantParkingInfoDTO.getTenantId());
        ThreadContext.put(LogConstant.TENANT_ID, tenantId);
        if (!checkSign(parkingParam, tenantParkingInfoDTO.getPlatformId())) {
            return result(ParkingReturnCodeEnum.SIGN_ERROR, "验签失败", null);
        }
        try {
            List<ParkingInParam> params = new ArrayList<>();
            if (redisUtils.tryLock(PARK_IN + LOCK + tenantId)) {
                try {
                    for (ParkingInParam param : parkingInParams) {
                        if (!redisUtils.hasKey(PARK_IN + param.getItemId())) {
                            redisUtils.set(PARK_IN + param.getItemId(), param.getItemId(), TIME);
                            params.add(param);
                        }
                    }
                } finally {
                    redisUtils.unlock(PARK_IN + LOCK + tenantId);
                }
            }
            if (Collections3.isEmpty(params)) {
                return result(ParkingReturnCodeEnum.SUCCESS, "", null);
            }
            //下发数据
            ResponseResult<Boolean> responseResult = parkingFeignClient.receiveInParkInfo(params);
            if (null == responseResult || !responseResult.getData()) {
                return result(ParkingReturnCodeEnum.FAIL, "入场数据下发失败", null);
            }
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        } catch (Exception e) {
            return result(ParkingReturnCodeEnum.FAIL, e.getMessage(), null);
        } finally {
            ThreadContext.remove(LogConstant.TENANT_ID);
        }
    }

    @Override
    public ParkingResultDTO parkOut(ParkingParam parkingParam) {
        List<ParkingOutParam> parkingOutParams = JsonUtils.jsonToList(parkingParam.getDataItems(), ParkingOutParam.class);
        if (Collections3.isEmpty(parkingOutParams)) {
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        }
        TenantParkingInfoDTO tenantParkingInfoDTO = getTenantId(parkingOutParams.get(0).getParkCode());
        String tenantId = String.valueOf(tenantParkingInfoDTO.getTenantId());
        ThreadContext.put(LogConstant.TENANT_ID, tenantId);
        if (!checkSign(parkingParam, tenantParkingInfoDTO.getPlatformId())) {
            return result(ParkingReturnCodeEnum.SIGN_ERROR, "验签失败", null);
        }
        try {

            List<ParkingOutParam> params = new ArrayList<>();
            if (redisUtils.tryLock(PARK_OUT + LOCK + tenantId)) {
                try {
                    for (ParkingOutParam param : parkingOutParams) {
                        if (!redisUtils.hasKey(PARK_OUT + param.getItemId())) {
                            redisUtils.set(PARK_OUT + param.getItemId(), param.getItemId(), TIME);
                            params.add(param);
                        }
                    }
                } finally {
                    redisUtils.unlock(PARK_OUT + LOCK + tenantId);
                }
            }
            if (Collections3.isEmpty(params)) {
                return result(ParkingReturnCodeEnum.SUCCESS, "", null);
            }
            //下发数据
            ResponseResult<Boolean> responseResult = parkingFeignClient.receiveOutParkInfo(params);
            if (null == responseResult || !responseResult.getData()) {
                return result(ParkingReturnCodeEnum.FAIL, "出场数据下发失败", null);
            }
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        } catch (Exception e) {
            return result(ParkingReturnCodeEnum.FAIL, e.getMessage(), null);
        } finally {
            ThreadContext.remove(LogConstant.TENANT_ID);
        }
    }

    @Override
    public ParkingResultDTO parkOrder(ParkingParam parkingParam) {
        List<ParkingOrderParam> parkingOrderParams = JsonUtils.jsonToList(parkingParam.getDataItems(), ParkingOrderParam.class);
        if (Collections3.isEmpty(parkingOrderParams)) {
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        }
        TenantParkingInfoDTO tenantParkingInfoDTO = getTenantId(parkingOrderParams.get(0).getParkCode());
        String tenantId = String.valueOf(tenantParkingInfoDTO.getTenantId());
        ThreadContext.put(LogConstant.TENANT_ID, tenantId);
        if (!checkSign(parkingParam, tenantParkingInfoDTO.getPlatformId())) {
            return result(ParkingReturnCodeEnum.SIGN_ERROR, "验签失败", null);
        }
        try {
            List<ParkingOrderParam> params = new ArrayList<>();
            if (redisUtils.tryLock(PARK_ORDER + LOCK + tenantId)) {
                try {
                    for (ParkingOrderParam param : parkingOrderParams) {
                        if (!redisUtils.hasKey(PARK_ORDER + param.getItemId())) {
                            redisUtils.set(PARK_ORDER + param.getItemId(), param.getItemId(), TIME);
                            params.add(param);
                        }
                    }
                } finally {
                    redisUtils.unlock(PARK_ORDER + LOCK + tenantId);
                }
            }
            if (Collections3.isEmpty(params)) {
                return result(ParkingReturnCodeEnum.SUCCESS, "", null);
            }
            //下发数据
            ResponseResult<Boolean> responseResult = parkingFeignClient.receiveParkOrder(params);
            if (null == responseResult || !responseResult.getData()) {
                return result(ParkingReturnCodeEnum.FAIL, "订单通知下发失败", null);
            }
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        } catch (Exception e) {
            return result(ParkingReturnCodeEnum.FAIL, e.getMessage(), null);
        } finally {
            ThreadContext.remove(LogConstant.TENANT_ID);
        }
    }

    @Override
    public ParkingResultDTO parkCharge(ParkingParam parkingParam) {
        List<ParkingChargeRecordParam> parkingChargeRecordParams = JsonUtils.jsonToList(parkingParam.getDataItems(), ParkingChargeRecordParam.class);
        if (Collections3.isEmpty(parkingChargeRecordParams)) {
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        }
        TenantParkingInfoDTO tenantParkingInfoDTO = getTenantId(parkingChargeRecordParams.get(0).getParkCode());
        String tenantId = String.valueOf(tenantParkingInfoDTO.getTenantId());
        ThreadContext.put(LogConstant.TENANT_ID, tenantId);
        if (!checkSign(parkingParam, tenantParkingInfoDTO.getPlatformId())) {
            return result(ParkingReturnCodeEnum.SIGN_ERROR, "验签失败", null);
        }
        try {
            List<ParkingChargeRecordParam> params = new ArrayList<>();
            if (redisUtils.tryLock(PARK_CHARGE + LOCK + tenantId)) {
                try {
                    for (ParkingChargeRecordParam param : parkingChargeRecordParams) {
                        if (!redisUtils.hasKey(PARK_CHARGE + param.getItemId())) {
                            redisUtils.set(PARK_CHARGE + param.getItemId(), param.getItemId(), TIME);
                            params.add(param);
                        }
                    }
                } finally {
                    redisUtils.unlock(PARK_CHARGE + LOCK + tenantId);
                }
            }
            if (Collections3.isEmpty(params)) {
                return result(ParkingReturnCodeEnum.SUCCESS, "", null);
            }
            //下发数据
            ResponseResult<Boolean> responseResult = parkingFeignClient.receiveParkCharge(params);
            if (null == responseResult || !responseResult.getData()) {
                return result(ParkingReturnCodeEnum.FAIL, "收费记录下发失败", null);
            }
            return result(ParkingReturnCodeEnum.SUCCESS, "", null);
        } catch (Exception e) {
            return result(ParkingReturnCodeEnum.FAIL, e.getMessage(), null);
        } finally {
            ThreadContext.remove(LogConstant.TENANT_ID);
        }
    }

    @Override
    public List<StatisticsCarDTO> statisticsCar(StatisticsCarQuery query) {
        //查询租户id
        ResponseResult<TenantDTO> responseResult = globalClient.queryTenantByCode(query.getMarketCode());
        if (null == responseResult || null == responseResult.getData()) {
            return Collections3.emptyList();
        }
        ThreadContext.put(LogConstant.TENANT_ID, String.valueOf(responseResult.getData().getId()));
        try {
            com.ibeeking.found.open.api.common.query.StatisticsCarQuery statisticsCarQuery = BeanUtil.convertBean(query, com.ibeeking.found.open.api.common.query.StatisticsCarQuery.class);
            ResponseResult<List<StatisticsCarDTO>> listResponseResult = parkingFeignClient.statisticsCar(statisticsCarQuery);
            return listResponseResult.getData();
        } finally {
            ThreadContext.remove(LogConstant.TENANT_ID);
        }
    }

    /**
     * 封装返参
     *
     * @param parkingReturnCode
     * @param message
     * @param items
     * @return
     */
    public ParkingResultDTO result(ParkingReturnCodeEnum parkingReturnCode, String message, List<ParkingResultDTO.ParkingResultItemDTO> items) {
        ParkingResultDTO result = new ParkingResultDTO();
        result.setResultCode(parkingReturnCode.getCode());
        result.setMessage(StringUtils.isBlank(message) ? "" : message);
        if (!Collections3.isEmpty(items)) {
            result.setDataItems(items);
        }
        return result;
    }

    /**
     * 签名校验
     *
     * @param param
     * @return
     */
    private Boolean checkSign(ParkingParam param, Long configId) {
        //查询签名key
        ParkingConfigQuery configQuery = new ParkingConfigQuery();
        configQuery.setConfigId(configId);
        ResponseResult<ParkingPlatformDTO> responseResult = parkingFeignClient.parkingConfig(configQuery);
        if (null == responseResult || null == responseResult.getData()) {
            return false;
        }
        String sign = EncryptUtils.Md5Encrypt.md5Str(responseResult.getData().getSignSecret() + param.getTs() + param.getDataItems()).toUpperCase(Locale.ROOT);
        return sign.equals(param.getSn());
    }

    private TenantParkingInfoDTO getTenantId(String parkCode) {
        if (StringUtils.isBlank(parkCode)) {
            throw new DataNotExistException("停车场编码不能为空");
        }
        ResponseResult<TenantParkingInfoDTO> responseResult = parkingFeignClient.queryTenantIdByParkCode(parkCode);
        if (null == responseResult || null == responseResult.getData()) {
            throw new DataNotExistException("查询停车场所属租户不存在");
        }
        return responseResult.getData();
    }

}
